﻿using System.Collections.Concurrent;
using System.Diagnostics.Contracts;
using System.Threading.Tasks;

namespace O1.IO
{
    [ContractVerification(true)]
    internal static class DmlCommandQueue
    {
        private static readonly BlockingCollection<Command> CommandQueue = new BlockingCollection<Command>();

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "Reference to executing thread.")]
        private static readonly Task CommandWorker = InitializeWorker();

        public static bool Cancel { get; set; }

        public static CommandHandle EnqueueCommand(byte[] control, byte[] buffer, long transactionId, short count)
        {
            Contract.Ensures(Contract.Result<CommandHandle>() != null);

            var command = new Command(control, buffer, transactionId, count, new CommandHandle());
            CommandQueue.Add(command);
            return command.Handle;
        }

        private static Task InitializeWorker()
        {
            return Task.Factory.StartNew(() =>
            {
                foreach (var command in CommandQueue.GetConsumingEnumerable())
                {
                    Contract.Assume(command.Handle != null);

                    // TODO: Process DML command, set up transaction scope and isolation level...etc.
                    // TX will always be on an isolated thread from the caller. Isolation level must be in cell data (version)
                    // TODO: the command buffer may have to allow for byte marks to identify Tx boundaries.
                    command.Handle.Complete = true;
                }
            });
        }

        private struct Command
        {
            public readonly byte[] Control;

            public readonly byte[] Buffer;

            public readonly long TransactionId;

            public readonly short Count;

            public readonly CommandHandle Handle;

            public Command(byte[] control, byte[] buffer, long transactionId, short count, CommandHandle handle) : this()
            {
                this.Control = control;
                this.Buffer = buffer;
                this.TransactionId = transactionId;
                this.Count = count;
                this.Handle = handle;
            }
        }

        internal class CommandHandle
        {
            public bool Complete { get; set; }
        }
    }
}
